home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Browsers, Managers & Extensions / Mozilla Weave 0.2.7 / latest-weave.xpi / modules / log4moz.js < prev    next >
Text File  |  2008-08-19  |  14KB  |  528 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is log4moz
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  * Michael Johnston
  18.  * Portions created by the Initial Developer are Copyright (C) 2006
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  * Michael Johnston <special.michael@gmail.com>
  23.  * Dan Mills <thunder@mozilla.com>
  24.  *
  25.  * Alternatively, the contents of this file may be used under the terms of
  26.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  27.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28.  * in which case the provisions of the GPL or the LGPL are applicable instead
  29.  * of those above. If you wish to allow use of your version of this file only
  30.  * under the terms of either the GPL or the LGPL, and not to allow others to
  31.  * use your version of this file under the terms of the MPL, indicate your
  32.  * decision by deleting the provisions above and replace them with the notice
  33.  * and other provisions required by the GPL or the LGPL. If you do not delete
  34.  * the provisions above, a recipient may use your version of this file under
  35.  * the terms of any one of the MPL, the GPL or the LGPL.
  36.  *
  37.  * ***** END LICENSE BLOCK ***** */
  38.  
  39. const EXPORTED_SYMBOLS = ['Log4Moz'];
  40.  
  41. const Cc = Components.classes;
  42. const Ci = Components.interfaces;
  43. const Cr = Components.results;
  44. const Cu = Components.utils;
  45.  
  46. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  47.  
  48. const MODE_RDONLY   = 0x01;
  49. const MODE_WRONLY   = 0x02;
  50. const MODE_CREATE   = 0x08;
  51. const MODE_APPEND   = 0x10;
  52. const MODE_TRUNCATE = 0x20;
  53.  
  54. const PERMS_FILE      = 0644;
  55. const PERMS_DIRECTORY = 0755;
  56.  
  57. const ONE_BYTE = 1;
  58. const ONE_KILOBYTE = 1024 * ONE_BYTE;
  59. const ONE_MEGABYTE = 1024 * ONE_KILOBYTE;
  60.  
  61. let Log4Moz = {
  62.   Level: {
  63.     Fatal:  70,
  64.     Error:  60,
  65.     Warn:   50,
  66.     Info:   40,
  67.     Config: 30,
  68.     Debug:  20,
  69.     Trace:  10,
  70.     All:    0,
  71.     Desc: {
  72.       70: "FATAL",
  73.       60: "ERROR",
  74.       50: "WARN",
  75.       40: "INFO",
  76.       30: "CONFIG",
  77.       20: "DEBUG",
  78.       10: "TRACE",
  79.       0:  "ALL"
  80.     }
  81.   },
  82.  
  83.   get Service() {
  84.     delete Log4Moz.Service;
  85.     Log4Moz.Service = new Log4MozService();
  86.     return Log4Moz.Service;
  87.   },
  88.  
  89.   get Formatter() { return Formatter; },
  90.   get BasicFormatter() { return BasicFormatter; },
  91.   get Appender() { return Appender; },
  92.   get DumpAppender() { return DumpAppender; },
  93.   get ConsoleAppender() { return ConsoleAppender; },
  94.   get FileAppender() { return FileAppender; },
  95.   get RotatingFileAppender() { return RotatingFileAppender; },
  96.  
  97.   // Logging helper:
  98.   // let logger = Log4Moz.Service.getLogger("foo");
  99.   // logger.info(Log4Moz.enumerateInterfaces(someObject).join(","));
  100.   enumerateInterfaces: function(aObject) {
  101.     let interfaces = [];
  102.  
  103.     for (i in Ci) {
  104.       try {
  105.         aObject.QueryInterface(Ci[i]);
  106.         interfaces.push(i);
  107.       }
  108.       catch(ex) {}
  109.     }
  110.  
  111.     return interfaces;
  112.   },
  113.  
  114.   // Logging helper:
  115.   // let logger = Log4Moz.Service.getLogger("foo");
  116.   // logger.info(Log4Moz.enumerateProperties(someObject).join(","));
  117.   enumerateProperties: function(aObject, aExcludeComplexTypes) {
  118.     let properties = [];
  119.  
  120.     for (p in aObject) {
  121.       try {
  122.         if (aExcludeComplexTypes &&
  123.             (typeof aObject[p] == "object" || typeof aObject[p] == "function"))
  124.           continue;
  125.         properties.push(p + " = " + aObject[p]);
  126.       }
  127.       catch(ex) {
  128.         properties.push(p + " = " + ex);
  129.       }
  130.     }
  131.  
  132.     return properties;
  133.   }
  134. };
  135.  
  136.  
  137. /*
  138.  * LogMessage
  139.  * Encapsulates a single log event's data
  140.  */
  141. function LogMessage(loggerName, level, message){
  142.   this.loggerName = loggerName;
  143.   this.message = message;
  144.   this.level = level;
  145.   this.time = Date.now();
  146. }
  147. LogMessage.prototype = {
  148.   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
  149.  
  150.   get levelDesc() {
  151.     if (this.level in Log4Moz.Level.Desc)
  152.       return Log4Moz.Level.Desc[this.level];
  153.     return "UNKNOWN";
  154.   },
  155.  
  156.   toString: function LogMsg_toString(){
  157.     return "LogMessage [" + this.time + " " + this.level + " " +
  158.       this.message + "]";
  159.   }
  160. };
  161.  
  162. /*
  163.  * Logger
  164.  * Hierarchical version.  Logs to all appenders, assigned or inherited
  165.  */
  166.  
  167. function Logger(name, repository) {
  168.   this._name = name;
  169.   this._repository = repository;
  170.   this._appenders = [];
  171. }
  172. Logger.prototype = {
  173.   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
  174.  
  175.   parent: null,
  176.  
  177.   _level: null,
  178.   get level() {
  179.     if (this._level != null)
  180.       return this._level;
  181.     if (this.parent)
  182.       return this.parent.level;
  183.     dump("log4moz warning: root logger configuration error: no level defined\n");
  184.     return Log4Moz.Level.All;
  185.   },
  186.   set level(level) {
  187.     this._level = level;
  188.   },
  189.  
  190.   _appenders: null,
  191.   get appenders() {
  192.     if (!this.parent)
  193.       return this._appenders;
  194.     return this._appenders.concat(this.parent.appenders);
  195.   },
  196.  
  197.   addAppender: function Logger_addAppender(appender) {
  198.     for (let i = 0; i < this._appenders.length; i++) {
  199.       if (this._appenders[i] == appender)
  200.         return;
  201.     }
  202.     this._appenders.push(appender);
  203.   },
  204.  
  205.   log: function Logger_log(message) {
  206.     if (this.level > message.level)
  207.       return;
  208.     let appenders = this.appenders;
  209.     for (let i = 0; i < appenders.length; i++){
  210.       appenders[i].append(message);
  211.     }
  212.   },
  213.  
  214.   fatal: function Logger_fatal(string) {
  215.     this.log(new LogMessage(this._name, Log4Moz.Level.Fatal, string));
  216.   },
  217.   error: function Logger_error(string) {
  218.     this.log(new LogMessage(this._name, Log4Moz.Level.Error, string));
  219.   },
  220.   warn: function Logger_warn(string) {
  221.     this.log(new LogMessage(this._name, Log4Moz.Level.Warn, string));
  222.   },
  223.   info: function Logger_info(string) {
  224.     this.log(new LogMessage(this._name, Log4Moz.Level.Info, string));
  225.   },
  226.   config: function Logger_config(string) {
  227.     this.log(new LogMessage(this._name, Log4Moz.Level.Config, string));
  228.   },
  229.   debug: function Logger_debug(string) {
  230.     this.log(new LogMessage(this._name, Log4Moz.Level.Debug, string));
  231.   },
  232.   trace: function Logger_trace(string) {
  233.     this.log(new LogMessage(this._name, Log4Moz.Level.Trace, string));
  234.   }
  235. };
  236.  
  237. /*
  238.  * LoggerRepository
  239.  * Implements a hierarchy of Loggers
  240.  */
  241.  
  242. function LoggerRepository() {}
  243. LoggerRepository.prototype = {
  244.   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
  245.  
  246.   _loggers: {},
  247.  
  248.   _rootLogger: null,
  249.   get rootLogger() {
  250.     if (!this._rootLogger) {
  251.       this._rootLogger = new Logger("root", this);
  252.       this._rootLogger.level = Log4Moz.Level.All;
  253.     }
  254.     return this._rootLogger;
  255.   },
  256.   // FIXME: need to update all parent values if we do this
  257.   //set rootLogger(logger) {
  258.   //  this._rootLogger = logger;
  259.   //},
  260.  
  261.   _updateParents: function LogRep__updateParents(name) {
  262.     let pieces = name.split('.');
  263.     let cur, parent;
  264.  
  265.     // find the closest parent
  266.     for (let i = 0; i < pieces.length; i++) {
  267.       if (cur)
  268.         cur += '.' + pieces[i];
  269.       else
  270.         cur = pieces[i];
  271.       if (cur in this._loggers)
  272.         parent = cur;
  273.     }
  274.  
  275.     // if they are the same it has no parent
  276.     if (parent == name)
  277.       this._loggers[name].parent = this.rootLogger;
  278.     else
  279.       this._loggers[name].parent = this._loggers[parent];
  280.  
  281.     // trigger updates for any possible descendants of this logger
  282.     for (let logger in this._loggers) {
  283.       if (logger != name && name.indexOf(logger) == 0)
  284.         this._updateParents(logger);
  285.     }
  286.   },
  287.  
  288.   getLogger: function LogRep_getLogger(name) {
  289.     if (!(name in this._loggers)) {
  290.       this._loggers[name] = new Logger(name, this);
  291.       this._updateParents(name);
  292.     }
  293.     return this._loggers[name];
  294.   }
  295. };
  296.  
  297. /*
  298.  * Formatters
  299.  * These massage a LogMessage into whatever output is desired
  300.  * Only the BasicFormatter is currently implemented
  301.  */
  302.  
  303. // Abstract formatter
  304. function Formatter() {}
  305. Formatter.prototype = {
  306.   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
  307.   format: function Formatter_format(message) {}
  308. };
  309.  
  310. // FIXME: should allow for formatting the whole string, not just the date
  311. function BasicFormatter(dateFormat) {
  312.   if (dateFormat)
  313.     this.dateFormat = dateFormat;
  314. }
  315. BasicFormatter.prototype = {
  316.   _dateFormat: null,
  317.  
  318.   get dateFormat() {
  319.     if (!this._dateFormat)
  320.       this._dateFormat = "%Y-%m-%d %H:%M:%S";
  321.     return this._dateFormat;
  322.   },
  323.  
  324.   set dateFormat(format) {
  325.     this._dateFormat = format;
  326.   },
  327.  
  328.   format: function BF_format(message) {
  329.     let date = new Date(message.time);
  330.     return date.toLocaleFormat(this.dateFormat) + "\t" +
  331.       message.loggerName + "\t" + message.levelDesc + "\t" +
  332.       message.message + "\n";
  333.   }
  334. };
  335. BasicFormatter.prototype.__proto__ = new Formatter();
  336.  
  337. /*
  338.  * Appenders
  339.  * These can be attached to Loggers to log to different places
  340.  * Simply subclass and override doAppend to implement a new one
  341.  */
  342.  
  343. function Appender(formatter) {
  344.   this._name = "Appender";
  345.   this._formatter = formatter? formatter : new BasicFormatter();
  346. }
  347. Appender.prototype = {
  348.   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
  349.  
  350.   _level: Log4Moz.Level.All,
  351.   get level() { return this._level; },
  352.   set level(level) { this._level = level; },
  353.  
  354.   append: function App_append(message) {
  355.     if(this._level <= message.level)
  356.       this.doAppend(this._formatter.format(message));
  357.   },
  358.   toString: function App_toString() {
  359.     return this._name + " [level=" + this._level +
  360.       ", formatter=" + this._formatter + "]";
  361.   },
  362.   doAppend: function App_doAppend(message) {}
  363. };
  364.  
  365. /*
  366.  * DumpAppender
  367.  * Logs to standard out
  368.  */
  369.  
  370. function DumpAppender(formatter) {
  371.   this._name = "DumpAppender";
  372.   this._formatter = formatter;
  373. }
  374. DumpAppender.prototype = {
  375.   doAppend: function DApp_doAppend(message) {
  376.     dump(message);
  377.   }
  378. };
  379. DumpAppender.prototype.__proto__ = new Appender();
  380.  
  381. /*
  382.  * ConsoleAppender
  383.  * Logs to the javascript console
  384.  */
  385.  
  386. function ConsoleAppender(formatter) {
  387.   this._name = "ConsoleAppender";
  388.   this._formatter = formatter;
  389. }
  390. ConsoleAppender.prototype = {
  391.   doAppend: function CApp_doAppend(message) {
  392.     if (message.level > Log4Moz.Level.Warn) {
  393.       Cu.reportError(message);
  394.       return;
  395.     }
  396.     Cc["@mozilla.org/consoleservice;1"].
  397.       getService(Ci.nsIConsoleService).logStringMessage(message);
  398.   }
  399. };
  400. ConsoleAppender.prototype.__proto__ = new Appender();
  401.  
  402. /*
  403.  * FileAppender
  404.  * Logs to a file
  405.  */
  406.  
  407. function FileAppender(file, formatter) {
  408.   this._name = "FileAppender";
  409.   this._file = file; // nsIFile
  410.   this._formatter = formatter;
  411. }
  412. FileAppender.prototype = {
  413.   __fos: null,
  414.   get _fos() {
  415.     if (!this.__fos)
  416.       this.openStream();
  417.     return this.__fos;
  418.   },
  419.  
  420.   openStream: function FApp_openStream() {
  421.     this.__fos = Cc["@mozilla.org/network/file-output-stream;1"].
  422.       createInstance(Ci.nsIFileOutputStream);
  423.     let flags = MODE_WRONLY | MODE_CREATE | MODE_APPEND;
  424.     this.__fos.init(this._file, flags, PERMS_FILE, 0);
  425.   },
  426.  
  427.   closeStream: function FApp_closeStream() {
  428.     if (!this.__fos)
  429.       return;
  430.     try {
  431.       this.__fos.close();
  432.       this.__fos = null;
  433.     } catch(e) {
  434.       dump("Failed to close file output stream\n" + e);
  435.     }
  436.   },
  437.  
  438.   doAppend: function FApp_doAppend(message) {
  439.     if (message === null || message.length <= 0)
  440.       return;
  441.     try {
  442.       this._fos().write(message, message.length);
  443.     } catch(e) {
  444.       dump("Error writing file:\n" + e);
  445.     }
  446.   },
  447.  
  448.   clear: function FApp_clear() {
  449.     this.closeStream();
  450.     this._file.remove(false);
  451.   }
  452. };
  453. FileAppender.prototype.__proto__ = new Appender();
  454.  
  455. /*
  456.  * RotatingFileAppender
  457.  * Similar to FileAppender, but rotates logs when they become too large
  458.  */
  459.  
  460. function RotatingFileAppender(file, formatter, maxSize, maxBackups) {
  461.   if (maxSize === undefined)
  462.     maxSize = ONE_MEGABYTE * 2;
  463.  
  464.   if (maxBackups === undefined)
  465.     maxBackups = 0;
  466.  
  467.   this._name = "RotatingFileAppender";
  468.   this._file = file; // nsIFile
  469.   this._formatter = formatter;
  470.   this._maxSize = maxSize;
  471.   this._maxBackups = maxBackups;
  472. }
  473. RotatingFileAppender.prototype = {
  474.   doAppend: function RFApp_doAppend(message) {
  475.     if (message === null || message.length <= 0)
  476.       return;
  477.     try {
  478.       this.rotateLogs();
  479.       this._fos.write(message, message.length);
  480.     } catch(e) {
  481.       dump("Error writing file:\n" + e);
  482.     }
  483.   },
  484.   rotateLogs: function RFApp_rotateLogs() {
  485.     if(this._file.exists() &&
  486.        this._file.fileSize < this._maxSize)
  487.       return;
  488.  
  489.     this.closeStream();
  490.  
  491.     for (let i = this.maxBackups - 1; i > 0; i--){
  492.       let backup = this._file.parent.clone();
  493.       backup.append(this._file.leafName + "." + i);
  494.       if (backup.exists())
  495.         backup.moveTo(this._file.parent, this._file.leafName + "." + (i + 1));
  496.     }
  497.  
  498.     let cur = this._file.clone();
  499.     if (cur.exists())
  500.       cur.moveTo(cur.parent, cur.leafName + ".1");
  501.  
  502.     // Note: this._file still points to the same file
  503.   }
  504. };
  505. RotatingFileAppender.prototype.__proto__ = new FileAppender();
  506.  
  507. /*
  508.  * LoggingService
  509.  */
  510.  
  511. function Log4MozService() {
  512.   this._repository = new LoggerRepository();
  513. }
  514. Log4MozService.prototype = {
  515.   //classDescription: "Log4moz Logging Service",
  516.   //contractID: "@mozilla.org/log4moz/service;1",
  517.   //classID: Components.ID("{a60e50d7-90b8-4a12-ad0c-79e6a1896978}"),
  518.   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
  519.  
  520.   get rootLogger() {
  521.     return this._repository.rootLogger;
  522.   },
  523.  
  524.   getLogger: function LogSvc_getLogger(name) {
  525.     return this._repository.getLogger(name);
  526.   }
  527. };
  528.